#include <windows.h>

#include "lib.h"

#include "bratconn.h"
#include "bratgame.h"
#include "bratsql.h"
#include "log.h"

#include "stdncerr.h"
#include "badpacketmsgs.h"
#include "BaccaratXMLTranslator.h"

BratConnection::BratConnection(Server* server, SOCKET client)
	: Connection(server, client)
{
	minBet = 500;	// purposefully set maxBet < minBet to disallow playing
	maxBet = 100;	// games where these values don't come from database
	compFactor = 0;

	loggedOn = false;

    casinoSpecific = (void*)0;

    // kharmon.  11-16-06.  #16921.
	msglen = 0;
	memset(msgbuf,0,sizeof(msgbuf));
    #ifdef FORCEDSHOE
    m_pid="";
    #endif

}

BratConnection::~BratConnection()
{
	//CSN-7075: New authentication
	trackLogoutSession();
	
	removeCasinoSpecific();
}

void
BratConnection::msgStart()
{
	msglen = 0;
}

void
BratConnection::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
}

void
BratConnection::msgAdd(int32 SignedLong)
{
	msgbuf[msglen++] = (uint8)((SignedLong) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 8) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 16) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 24) & 0xff);
}

uint8*
BratConnection::msgGetBuf()
{
	return msgbuf + msglen;
}

void
BratConnection::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

void
BratConnection::msgEnd()
{
	msglen = encode(msgbuf, obuf + oused + 1, msglen);
	msglen++;	// include msg len byte
	obuf[oused] = (uint8)(msglen & 0xff);
	oused += msglen;
}

void
BratConnection::sendStopMsg()
{
	msgStart();
	msgAdd((uint8)'!');
	msgEnd();
}

int32
BratConnection::msgRemove(uint8* Src, int32* pResult)
{
	*pResult = (int32)(
		(Src[0] & 0xFF) |
		((Src[1] << 8) & 0xFF00) |
		((Src[2] << 16) & 0xFF0000) |
		((Src[3] << 24) & 0xFF000000));

	return 4;
}

bool8
BratConnection::getResponse()
{
	int32	ieat	= 0;
	int32	remain	= iused;
	uint8 *	src		= ibuf;
	int32	message_length;
	int32	decode_length;
	int32	balance;

	// [AO 2007-10-01] We can send just one reply for certain repeating messages.
	// We're not handling partial buffers and incomplete messages but that's OK.
	char last_message = 0;

	// Bacarrat utilizes a one byte size field, hence we need as least one
	// byte for any given message.
	while (remain >= 1)
	{
		message_length = src[0];
	
		// Reject invalid message lengths of zero and one - because they do
		// no contain payload data.
		if (message_length <= 1)
		{
			ieat	+= 1;
			remain	-= 1;
			src		+= 1;
			continue;
		}

		// Reject invalid message lengths which exceed the length of our
		// receive buffer.
		if (message_length > INMAX)
		{
			DEBUGMSG(("BratConnection::getResponse:"
				"  message length exceeds buffer size  "));
			ieat = iused;
			break;
		}

		// Incomplete message - must wait for more data.
		if (remain < message_length)
			break;

        // kharmon.  11-16-06.  #16921.
	    msglen = 0;
	    memset(msgbuf,0,sizeof(msgbuf));

		// Well-formed, but not necessarily valid, message.
		decode_length = decode(src + 1, msgbuf, message_length - 1);
		ieat	+= message_length;
		remain	-= message_length;
		src		+= message_length;

		switch (msgbuf[0])
		{
			// ANSI Login
			case 'L':
			{
				int32 err = 0;

				if (!parseCasinoLogin(5, &err))
				{
					msgStart();
					msgAdd((uint8)'E');
					msgAdd((uint8)(err & 0xff));
					msgEnd();
					break;
				}

				handleLogin(decode_length);
				break;
			}

			// UNICODE Login
			case 'M':
			{
				switch (msgbuf[1])
				{
					case 'L':
					{
						int32 err = 0;

						if (!parseUnicodeCasinoLogin(6, &err))
						{
							msgStart();
							msgAdd((uint8)'E');
							msgAdd((uint8)(err & 0xff));
							msgEnd();
							break;
						}

						handleLogin(decode_length);
						break;
					}

					default:
					{
						DEBUGMSG(("BratConnection::getResponse() unknown "
							"unicode message: 0x%02x", msgbuf[1]));

						// Server Network Audit: Vulnerability #2002120901
						//
#ifdef DEBUG			// FIX - SNA:Vulnerability #2002120901
						msgStart();
						msgAdd((uint8)'N');
						msgEnd();
#endif					// FIX - SNA:Vulnerability #2002120901
						break;
					}
				}

				break;
			}

			case 'D':
			{
				if (!loggedOn)
				{
					Log::msgWarning("Deal: not logged on...invalid bet  ");

					trackaddBadPacket(TRY_DEAL_NOT_LOGGED_ON, msgbuf[0],
						(char*)msgbuf, decode_length);

					// REVISIT: SNA Vulnerability: #2002120901
					msgStart();
					msgAdd((uint8)'B');
					msgEnd();

					break;
				}

				if (isServerStopping())
				{
					sendStopMsg();
					break;
				}

				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

				int32 i, bets[BratGame::NUMBETS], totalBet, validMin, tooSmall;

				i = 1;
				i += msgRemove(msgbuf + i, bets + BratGame::BANKER);
				i += msgRemove(msgbuf + i, bets + BratGame::PLAYER);
				i += msgRemove(msgbuf + i, bets + BratGame::TIE);

				totalBet = 0;
				validMin = tooSmall = BratGame::NUMBETS;
				for (i = totalBet = 0; i < BratGame::NUMBETS; i++)
				{
					totalBet += bets[i];

					if (bets[i] < 0)
						tooSmall = i;

					if (bets[i] >= minBet)
						validMin = i;
				}

				trackGetBalance(&balance); 

				if (balance > 0 && balance >= totalBet &&
					totalBet <= maxBet &&
					tooSmall == BratGame::NUMBETS && // no bets < 0
					validMin != BratGame::NUMBETS)	 // at least one between min/max
				{
					if (!BratGame::deal(this, bets, balance))
					{
						Log::msgError("  can't create game  ");

						// REVISIT: SNA Vulnerability: #2002120901
						msgStart();
						msgAdd((uint8)'N');
						msgEnd();
					}
				}
				else if (balance > 0)	// bad bet
				{
					if (tooSmall != BratGame::NUMBETS)
						i = tooSmall;
					else
						i = BratGame::NUMBETS;

					Log::msgWarning("Spin: invalid bet  ");

					// REVISIT: SNA Vulnerability: #2002120901
					msgStart();
					msgAdd((uint8)'B');
					msgAdd((uint8)(i & 0xff));
					msgEnd();
				}
				else					// not enough money in account
				{
					Log::msgWarning("Spin: out of money  ");

					// REVISIT: SNA Vulnerability: #2002120901
					msgStart();
					msgAdd((uint8)'$');
					msgEnd();
				}
				break;
			}

            case 'm':  // Balance Request
            {
				if (isServerStopping())
	            {
		            sendStopMsg();
		            break;
	            }

				// [AO 2007-10-01] Ignore repeating messages
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (msgbuf[0] == last_message || !ValidateSession())
					break;

	            //rule1: gotta be logged on
				int32 balance;
                if( loggedOn && trackGetBalance(&balance) )
                {
					msgStart();
					msgAdd((uint8)'m');
					msgAdd((int32)balance);
					msgEnd();
                }

	            break;
            }

			default:
			{

				trackaddBadPacket(GETREPONSE_DEFAULT_CASE, msgbuf[0],
					(char*)msgbuf, decode_length);

				Log::msgWarning("Connection::getResponse:  Unknown input"
					" (0x%02x)  ", msgbuf[0]);

				// Server Network Audit: Vulnerability #2002120901
				//
#ifdef DEBUG	// FIX - SNA:Vulnerability #2002120901
				msgStart();
				msgAdd((uint8)'N');
				msgEnd();
#endif			// FIX - SNA:Vulnerability #2002120901
				break;
			}
		}

		last_message = msgbuf[0]; // [AO 2007-10-01] Handle repeating messages
	}

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	return (bool8)(oused != 0);
}

void
BratConnection::handleLogin(int32 length)
{
	int32	balance;

	if (isServerStopping())
	{
		sendStopMsg();
		return;
	}

	if (! getOptions())
	{
		msgStart();
		msgAdd((uint8)'E');
		msgAdd((uint8)(0 & 0xff));
		msgEnd();
		return;
	}

	if (!loggedOn)
	{
		if( !trackGetPlayerLimits(&minBet, &maxBet, &compFactor) ||
			!trackGetBalance(&balance)  )
		{
			msgStart();
			msgAdd((uint8)'N');
			msgEnd();
			return;
		}

		msgStart();
		msgAdd((uint8)'L');
		msgAdd(balance);
		msgAdd(minBet);
		msgAdd(maxBet);
		msgAdd((int32) tiePayout);
		msgEnd();

		loggedOn = true;
	}
	else
	{
		Log::msgWarning("Login: attempt to re-log on  ");

		trackaddBadPacket(TRY_LOGON_WHEN_LOGGED_ON, msgbuf[0],
			(char*)msgbuf, length);

		// REVISIT: SNA Vulnerability: #2002120901
		msgStart();
		msgAdd((uint8)'N');
		msgEnd();
	}
}

bool
BratConnection::getOptions()
{
    bool bRetVal = false;
	
	//default is 9 to 1
	tiePayout = NINE_TO_ONE;
	int32 payout_index = -1;

    if (trackGetOptions(&payout_index))
    {
        bRetVal = true;
		tiePayout = (TiePayoutOdds) payout_index;
    }

    return bRetVal;
}

void 
BratConnection::allocateXMLTranslator( XMLTranslator** target )
{
	*target = new BaccaratXMLTranslator( getServer(), this );
}

// [AO 2008-01-16] Refactoring for HA
bool BratConnection::ValidateSession()
{
	int32 session_check = trackValidateSession();
	if (!session_check) // session is valid
		return true;

	// [AO 2008-01-22] Additional case from RTG 6.6.0 code drop
	if (session_check == BratSQL::NormalLogout)
		session_check = LOGINERR_NOTLOGGEDIN;

	msgStart();
	msgAdd(static_cast<uint8>('N'));
	msgAdd(static_cast<int32>(session_check));
	msgEnd();
	return false;
}
